package com.genreate.util.pay.wx.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.wechat.pay.java.core.notification.Resource;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.regex.Pattern;

/**
 * wx支付签名验证器
 *
 * @author LYS
 * @date 2025-07-12
 */
public class WxPaySignatureValidator {
    /**
     * 验证微信支付回调签名
     *
     * @param wechatpaySerial    验签的微信支付平台证书序列号/微信支付公钥ID
     * @param wechatpaySignature 验签的签名值
     * @param wechatpayTimestamp 验签的时间戳
     * @param wechatpayNonce     验签的随机字符串
     * @param requestBody        请求正文
     * @param publicKeyContent   微信支付公钥内容
     * @param certContent        平台证书内容
     * @return boolean
     * @throws Exception 例外
     */
    public static boolean validateSignature(
            String wechatpaySerial,
            String wechatpaySignature,
            String wechatpayTimestamp,
            String wechatpayNonce,
            String requestBody,
            String publicKeyContent,
            String certContent
    ) throws Exception {
        // 1. 构建验签串
        String message = buildMessage(wechatpayTimestamp, wechatpayNonce, requestBody);

        // 2. 判断使用公钥还是证书验签
        PublicKey publicKey;
        if (isPublicKeySerial(wechatpaySerial)) {
            // 使用微信支付公钥验签
            publicKey = getPublicKeyFromContent(publicKeyContent);
        } else {
            // 使用平台证书公钥验签
            X509Certificate certificate = getCertificateFromContent(certContent);
            publicKey = certificate.getPublicKey();
        }

        // 3. 验证签名
        return verifySignature(publicKey, wechatpaySignature, message);
    }

    /**
     * 构建验签串：timestamp\nnonce\nrequest_body\n
     *
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @param body      车身
     * @return {@link String }
     */
    private static String buildMessage(String timestamp, String nonce, String body) {
        return String.format("%s\n%s\n%s\n", timestamp, nonce, body);
    }

    /**
     * 判断是否为微信支付公钥序列号
     *
     * @param serial 串行
     * @return boolean
     */
    private static boolean isPublicKeySerial(String serial) {
        return Pattern.matches("^PUB_KEY_ID_\\d+$", serial);
    }

    /**
     * 从字符串内容加载公钥
     *
     * @param publicKeyContent 公钥内容
     * @return {@link PublicKey }
     * @throws Exception 例外
     */
    private static PublicKey getPublicKeyFromContent(String publicKeyContent) throws Exception {
        // 移除BEGIN/END标记和换行符
        String keyContent = publicKeyContent
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s+", "");

        byte[] keyBytes = Base64.getDecoder().decode(keyContent);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(new java.security.spec.X509EncodedKeySpec(keyBytes));
    }

    /**
     * 从字符串内容加载证书
     *
     * @param certContent 证书内容
     * @return {@link X509Certificate }
     * @throws Exception 例外
     */
    private static X509Certificate getCertificateFromContent(String certContent) throws Exception {
        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        return (X509Certificate) factory.generateCertificate(
                new java.io.ByteArrayInputStream(certContent.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 验证RSA SHA256签名
     *
     * @param publicKey       公钥
     * @param signatureBase64 签名base64
     * @param message         信息
     * @return boolean
     * @throws Exception 例外
     */
    private static boolean verifySignature(PublicKey publicKey, String signatureBase64, String message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initVerify(publicKey);
        sign.update(message.getBytes(StandardCharsets.UTF_8));
        return sign.verify(Base64.getDecoder().decode(signatureBase64));
    }


    /**
     * 解密微信支付回调通知中的resource.ciphertext
     *
     * @param apiV3Key       APIv3密钥
     * @param nonce          加密随机串 resource.nonce
     * @param associatedData 附加数据 resource.associated_data
     * @param ciphertext     密文 resource.ciphertext
     * @return 解密后的JSON字符串
     * @throws Exception 解密异常
     */
    public static String decrypt(String apiV3Key, String nonce, String associatedData, String ciphertext) {
        // 检查加密算法（目前仅支持AEAD_AES_256_GCM）

        try {
            // 1. 转换APIv3密钥为字节数组
            byte[] keyBytes = apiV3Key.getBytes(StandardCharsets.UTF_8);

            // 2. 解密ciphertext（Base64编码）
            byte[] ciphertextBytes = Base64.getDecoder().decode(ciphertext);

            // 3. 初始化AES-GCM解密器
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            GCMParameterSpec parameterSpec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));

            // 4. 初始化并执行解密
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameterSpec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            byte[] decryptedBytes = cipher.doFinal(ciphertextBytes);

            // 5. 返回解密后的JSON字符串
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        }catch (Exception e){
            e.printStackTrace();
        }
        throw new RuntimeException("解密失败");
    }

    /**
     * 解密微信支付回调通知中的resource.ciphertext
     *
     * @param apiV3Key APIv3密钥
     * @param nonce    加密随机串 resource.nonce
     * @param resource 资源
     * @return 解密后的JSON字符串
     * @throws Exception 例外
     */
    public static String decrypt(String apiV3Key, String nonce, Resource resource) {
        return decrypt(apiV3Key, nonce, resource.getAssociatedData(), resource.getCiphertext());
    }

    /**
     * 获取签名
     *
     * @param appId    应用程序id
     * @param prepayId 预付费id
     * @return {@link String }
     */
    public static String getSign(String appId, String prepayId, PrivateKey privateKey) {
        int second = DateUtil.date().second();
        String randomStr = RandomUtil.randomString(30);
        /**
         * 构造签名串
         * appid\n
         * 时间戳\n
         * 随机字符串\n
         * prepay_id\n
         */
        String buildSign = StrUtil.format("{}\n{}\n{}\n{]\n", appId, second, randomStr, prepayId);
        return generateSign(buildSign, privateKey);
    }


    /**
     * 计算微信签名
     *
     * @param signStr    标记str
     * @param privateKey 私钥
     * @return {@link String }
     */
    public static String generateSign(String signStr, PrivateKey privateKey){
        try {
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(signStr.getBytes("UTF-8"));
            byte[] signed = signature.sign();
            return Base64.getEncoder().encodeToString(signed);
        }catch (Exception e){
            e.printStackTrace();
        }
        throw new RuntimeException("计算微信签名出错");
    }
}